package com.myk.game.gobangchess.rooms.oldrooms;

import java.io.*;
import java.util.*;

import com.google.gson.annotations.Expose;
import com.myk.game.gobangchess.CustomBottom;
import com.myk.game.gobangchess.DataManager;
import com.myk.game.gobangchess.Room;
import com.myk.game.gobangchess.Sign;
import com.myk.game.gobangchess.constants.BoardStyleData;
import com.myk.game.gobangchess.constants.PathCost;
import com.myk.game.gobangchess.rooms.absrooms.movechess.MoreColorCost;
import com.myk.game.gobangchess.signs.ColorSign;
import com.myk.game.gobangchess.signs.GroundSign;
import com.myk.game.gobangchess.signs.LineSign;
import com.myk.game.gobangchess.utils.FileUtils;
import com.myk.game.gobangchess.utils.GridsUtils;
import com.myk.game.gobangchess.utils.Pair;

/**
 * 新无规则画板
 */
public class NoRuleNewRoom extends Room {
    static String roomDataPath = PathCost.ROOM_DATA_DIR + NoRuleNewRoom.class.getSimpleName() + "/";
    static String gridCacheFile = roomDataPath + "gridCache.txt";
    static String gameCacheFile = roomDataPath + "gameCache.txt";

    RoomCache roomCache = new RoomCache();

    LinkedList<ConfigMode> modeStep = new LinkedList<>();

    enum ConfigMode {
        MAIN_CONFIG,
        GRID_CONFIG,
        RESIZE_ADD_CONFIG,
        RESIZE_REDUCE_CONFIG,
        FAST_CONFIG,
        DRAW_CONFIG,
        CHESS_CONFIG,
        CACHE_CONFIG,

        MAIN_GAME,
    }

    CustomBottom cb_settingGrid = new CustomBottom(this, "盘面设置", (player) -> {
        modeStep.add(ConfigMode.GRID_CONFIG);
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_resizeGrid = new CustomBottom(this, "棋盘大小", (player) -> {
        modeStep.add(ConfigMode.RESIZE_ADD_CONFIG);
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_addGridRow = new CustomBottom(this, "增加一行", (player) -> {
        boolean ok = roomCache.curGridCache.resizeRow(1);
        if (!ok) {
            noticeAllBroadcast("行数只能10~20");
            return;
        }
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_addGridCol = new CustomBottom(this, "增加一列", (player) -> {
        boolean ok = roomCache.curGridCache.resizeCol(1);
        if (!ok) {
            noticeAllBroadcast("列数只能10~20");
            return;
        }
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_reduceGridRow = new CustomBottom(this, "减少一行", (player) -> {
        boolean ok = roomCache.curGridCache.resizeRow(-1);
        if (!ok) {
            noticeAllBroadcast("行数只能10~20");
            return;
        }
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_reduceGridCol = new CustomBottom(this, "减少一列", (player) -> {
        boolean ok = roomCache.curGridCache.resizeCol(-1);
        if (!ok) {
            noticeAllBroadcast("列数只能10~20");
            return;
        }
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_resizeGridMode = new CustomBottom(this, "切换增减", (player) -> {
        if (modeStep.getLast() == ConfigMode.RESIZE_REDUCE_CONFIG) {
            modeStep.removeLast();
            modeStep.add(ConfigMode.RESIZE_ADD_CONFIG);
        } else if (modeStep.getLast() == ConfigMode.RESIZE_ADD_CONFIG) {
            modeStep.removeLast();
            modeStep.add(ConfigMode.RESIZE_REDUCE_CONFIG);
        }
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_fastSetGrid = new CustomBottom(this, "快捷设置", (player) -> {
        modeStep.add(ConfigMode.FAST_CONFIG);
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_fastSetDefaultGrid = new CustomBottom(this, "一键纯色格", (player) -> {
        roomCache.curGridCache.gridSigns.clear();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_fastSetBlackWhiteGrid = new CustomBottom(this, "一键黑白格", (player) -> {
        ArrayList<Sign> chessboardStyle = new ArrayList<>();
        int row = roomCache.curGridCache.rowSize;
        int col = roomCache.curGridCache.colSize;
        //绘制类似国际象棋的黑白相间的格子
        ArrayList<Integer> bGrids = new ArrayList<>();
        ArrayList<Integer> wGrids = new ArrayList<>();
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int index = getIndex(i, j);
                if ((i + j) % 2 == 0) {
                    bGrids.add(index);
                } else {
                    wGrids.add(index);
                }
            }
        }
        chessboardStyle.add(new ColorSign(bGrids, "#FF888888"));
        chessboardStyle.add(new ColorSign(wGrids, "#FFDDDDDD"));
        roomCache.curGridCache.gridSigns = chessboardStyle;

        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_fastSetCrossedGrid = new CustomBottom(this, "一键交叉点", (player) -> {
        ArrayList<Sign> chessboardStyle = new ArrayList<>();
        int row = roomCache.curGridCache.rowSize;
        int col = roomCache.curGridCache.colSize;
        //背景
        chessboardStyle.add(new GroundSign(getIndex(0, 0), getIndex(row - 1, col - 1), MoreColorCost.BOARD_BACKGROUND, MoreColorCost.BOARD_BACKGROUND, 1.8f, 0, 0));
        //线条
        for (int i = 0; i < col; i++) {
            chessboardStyle.add(new LineSign(getIndex(0, i), getIndex(row - 1, i), MoreColorCost.BOARD_LINE, 0));
        }
        for (int i = 0; i < row; i++) {
            chessboardStyle.add(new LineSign(getIndex(i, 0), getIndex(i, col - 1), MoreColorCost.BOARD_LINE, 0));
        }
        roomCache.curGridCache.gridSigns = chessboardStyle;

        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_settingChess = new CustomBottom(this, "棋子设置", (player) -> {
        modeStep.add(ConfigMode.CHESS_CONFIG);
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_cacheGrid = new CustomBottom(this, "设置存档", (player) -> {
        modeStep.add(ConfigMode.CACHE_CONFIG);
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_saveGrid = new CustomBottom(this, "保存设置", (player) -> {
        FileUtils.createOrExistsDir(roomDataPath);

        this.roomCache.gridCaches.add(this.roomCache.curGridCache);
        while (this.roomCache.gridCaches.size() > 10) {
            this.roomCache.gridCaches.removeFirst();
        }
        try (FileOutputStream fileOutputStream = new FileOutputStream(gridCacheFile);
             BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
             DataOutputStream dos = new DataOutputStream(bufferedOutputStream)) {
            this.roomCache.serialize(dos);
        } catch (IOException e) {
            e.printStackTrace();
            noticeAllBroadcast("保存失败");
            return;
        }
        noticeAllBroadcast("保存成功");
    });

    CustomBottom cb_loadGrid = new CustomBottom(this, "还原设置", (player) -> {
        FileUtils.createOrExistsDir(roomDataPath);
        if (this.roomCache.gridCaches.isEmpty()) {
            noticeAllBroadcast("无可用存档");
            return;
        }
        this.roomCache.curGridCache = this.roomCache.gridCaches.removeLast();
        try (FileOutputStream fileOutputStream = new FileOutputStream(gridCacheFile);
             BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
             DataOutputStream dos = new DataOutputStream(bufferedOutputStream)) {
            this.roomCache.serialize(dos);
        } catch (IOException e) {
            e.printStackTrace();
            noticeAllBroadcast("还原失败");
            return;
        }
        noticeAllBroadcast("还原成功");
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_start = new CustomBottom(this, "开始游戏", (player) -> {
        modeStep.add(ConfigMode.MAIN_GAME);
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    CustomBottom cb_return = new CustomBottom(this, "返回上级", (player) -> {
        modeStep.removeLast();
        if (modeStep.isEmpty()) {
            modeStep.add(ConfigMode.MAIN_CONFIG);
        }
        refreshCusBottoms();
        noticeAllRefreshGameInfo();
    });

    private void refreshCusBottoms() {
        switch (modeStep.getLast()) {
            case MAIN_CONFIG:
                refreshCusBottoms(cb_settingGrid, cb_settingChess, cb_cacheGrid, cb_start);
                return;
            case GRID_CONFIG:
                refreshCusBottoms(cb_resizeGrid, cb_fastSetGrid, cb_return);
                return;
            case RESIZE_ADD_CONFIG:
                refreshCusBottoms(cb_addGridRow, cb_addGridCol, cb_resizeGridMode, cb_return);
                return;
            case RESIZE_REDUCE_CONFIG:
                refreshCusBottoms(cb_reduceGridRow, cb_reduceGridCol, cb_resizeGridMode, cb_return);
                return;
            case FAST_CONFIG:
                refreshCusBottoms(cb_fastSetDefaultGrid, cb_fastSetBlackWhiteGrid, cb_fastSetCrossedGrid, cb_return);
                return;
            case DRAW_CONFIG:
                refreshCusBottoms(cb_return);
                return;
            case CHESS_CONFIG:
                refreshCusBottoms(cb_return);
                return;
            case CACHE_CONFIG:
                refreshCusBottoms(cb_saveGrid, cb_loadGrid, cb_return);
                return;
            case MAIN_GAME:
                refreshCusBottoms(cb_return);
                return;
        }
    }

    private void refreshCusBottoms(CustomBottom... cbs) {
        if (player_black != null) {
            player_black.clearCustomBottom();
            player_black.dispatchCustomBottom();
            player_black.registerCustomBottom(cbs);
        }
        if (player_white != null && player_white != player_black) {
            player_white.clearCustomBottom();
            player_white.dispatchCustomBottom();
            player_white.registerCustomBottom(cbs);
        }
    }

    public NoRuleNewRoom(String roomName) {
        super(roomName, "\t\t可以自由的摆放棋子、管理布局、修改棋盘，没有具体的规则。", "新无规则画板", BoardStyleData.getGoBangStyle(9, 9), true,
                false);
        setSortingBottomValue(true); // 无规则画板永远置底
    }

    @Override
    public String getRuleText() {
        return "可以自由的摆放棋子、管理布局、修改棋盘，没有具体的规则。\n点击落座后一个人也能开始布局。\n注意下方的自定义按钮”切换模式“可以切换按钮菜单。";
    }

    @Override
    public String getGameInfo() {
        //先全部初始化
        curGrids = roomCache.curGridCache.createInitGrids();
        signList.clear();
        signList.addAll(roomCache.curGridCache.gridSigns);
        for (Map.Entry<Pair<Integer, Integer>, ChessStyle> entry : roomCache.curGameCache.gameGridValues.entrySet()) {
            entry.getValue().addTo(signList, curGrids, entry.getKey().first, entry.getKey().second);
        }
        if (!modeStep.isEmpty() && modeStep.getLast()== ConfigMode.CHESS_CONFIG) {
            this.roomCache.showChessSelect(signList, curGrids);
        }
        return super.getGameInfo();
    }

    @Override
    protected void doStart() {
        DataManager.noticeAllGameStart(player_black, player_white, roomName);
        overFlag = 1;// 重置结束状态：对弈进行中未结束
        playingStartTime = System.currentTimeMillis(); // 重置对弈开始时间
        resetGrids();
        refreshCusBottoms();
        noticeNoMove();
        preHistory();
    }

    public void resetGrids() {
        step = 0;
        gameTip = "-1";
        roomCache.curGameCache = new GameCache();
        modeStep.clear();
        modeStep.add(ConfigMode.MAIN_CONFIG);

        FileUtils.createOrExistsDir(roomDataPath);
        try (FileInputStream fileInputStream = new FileInputStream(gridCacheFile);
             BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
             DataInputStream dis = new DataInputStream(bufferedInputStream)) {
            this.roomCache = new RoomCache(dis);
        } catch (IOException e) {
            e.printStackTrace();
            this.roomCache = new RoomCache();
        }
    }

    @Override
    public void doRule(int row, int col, int value) {
        switch (modeStep.getLast()) {
            case CHESS_CONFIG:
                final ChessStyle style = ChessStyle.from(this.roomCache.curGameCache.gameGridValues.get(new Pair<>(row, col)).value);
                if (style == ChessStyle.EMPTY) {
                    return;
                } else if (this.roomCache.curGridCache.chessAvailable.contains(style)) {
                    this.roomCache.curGridCache.chessAvailable.remove(style);
                } else {
                    this.roomCache.curGridCache.chessAvailable.add(style);
                }
                break;
        }
    }

    private static class RoomCache {
        LinkedList<GridCache> gridCaches = new LinkedList<>();
        LinkedList<GameCache> gameCaches = new LinkedList<>();
        @Expose
        GridCache curGridCache;
        @Expose
        GameCache curGameCache;

        public RoomCache() {
            curGridCache = new GridCache();
            curGameCache = new GameCache();
        }

        public void serialize(DataOutputStream dos) throws IOException {
            dos.writeInt(this.gridCaches.size());
            for (GridCache cache : this.gridCaches) {
                cache.serialize(dos);
            }
        }

        public RoomCache(DataInputStream dis) throws IOException {
            this();
            int gridCacheSize = dis.readInt();
            this.gridCaches = new LinkedList<>();
            for (int i = 0; i < gridCacheSize; i++) {
                this.gridCaches.add(new GridCache(dis));
            }
        }

        public void showChessSelect(List<Sign> signList, int[][] grids) {
            int availableX = curGridCache.rowSize - 1;
            int availableY = 0;
            int unavailableX = 0;
            int unavailableY = 0;
            for (ChessStyle style : ChessStyle.values()) {
                if (curGridCache.chessAvailable.contains(style)) {
                    style.addTo(signList, grids, availableX, availableY);
                    availableY += 1;
                    if (availableY >= curGridCache.colSize) {
                        availableY -= curGridCache.colSize;
                        availableX += 1;
                    }
                } else {
                    style.addTo(signList, grids, unavailableX, unavailableY);
                    unavailableY += 1;
                    if (unavailableY >= curGridCache.colSize) {
                        unavailableY -= curGridCache.colSize;
                        unavailableX += 1;
                    }
                }
            }
        }
    }

    private static class GridCache {
        int rowSize = 15;
        int colSize = 15;
        HashMap<Pair<Integer, Integer>, ChessStyle> gridValues = new HashMap<>();
        ArrayList<Sign> gridSigns = new ArrayList<>();
        HashSet<ChessStyle> chessAvailable = new HashSet<>();

        public GridCache() {
            chessAvailable.add(ChessStyle.EMPTY);
            chessAvailable.add(ChessStyle.BLACK_CIRCLE);
            chessAvailable.add(ChessStyle.WHITE_CIRCLE);
        }

        public boolean resizeRow(int delta) {
            int newRowSize = rowSize + delta;
            if (newRowSize >= 10 && newRowSize <= 20) {
                rowSize = newRowSize;
                return true;
            }
            return false;
        }

        public boolean resizeCol(int delta) {
            int newColSize = colSize + delta;
            if (newColSize >= 10 && newColSize <= 20) {
                colSize = newColSize;
                return true;
            }
            return false;
        }

        public int[][] createInitGrids() {
            int[][] grid = GridsUtils.createEmptyGrids(rowSize, colSize);
            gridValues.forEach((k, v) -> {
                grid[k.first][k.second] = v.value;
            });
            return grid;
        }

        public void serialize(DataOutputStream dos) throws IOException {
            dos.writeInt(rowSize);
            dos.writeInt(colSize);
            dos.writeInt(this.gridValues.size());
            for (Map.Entry<Pair<Integer, Integer>, ChessStyle> entry : this.gridValues.entrySet()) {
                dos.writeInt(entry.getKey().first);
                dos.writeInt(entry.getKey().second);
                dos.writeInt(entry.getValue().value);
            }
            dos.writeInt(this.gridSigns.size());
            for (Sign sign : this.gridSigns) {
                dos.writeUTF(sign.getType());
                sign.serialize(dos);
            }
            dos.writeInt(this.chessAvailable.size());
            for (ChessStyle style : this.chessAvailable) {
                dos.writeInt(style.value);
            }
        }

        public GridCache(DataInputStream dis) throws IOException {
            this.rowSize = dis.readInt();
            this.colSize = dis.readInt();
            int gridValueSize = dis.readInt();
            this.gridValues = new HashMap<>(gridValueSize);
            for (int i = 0; i < gridValueSize; i++) {
                this.gridValues.put(new Pair<>(dis.readInt(), dis.readInt()), ChessStyle.from(dis.readInt()));
            }
            int gridSignSize = dis.readInt();
            this.gridSigns = new ArrayList<>(gridSignSize);
            for (int i = 0; i < gridSignSize; i++) {
                String type = dis.readUTF();
                switch (type) {
                    case "ColorSign":
                        this.gridSigns.add(new ColorSign(dis));
                        break;
                    case "LineSign":
                        this.gridSigns.add(new LineSign(dis));
                        break;
                    case "GroundSign":
                        this.gridSigns.add(new GroundSign(dis));
                        break;
                }
            }
            int chessAvailableSize = dis.readInt();
            this.chessAvailable = new HashSet<>(chessAvailableSize);
            for (int i = 0; i < chessAvailableSize; i++) {
                this.chessAvailable.add(ChessStyle.from(dis.readInt()));
            }
        }
    }

    private static class GameCache {
        HashMap<Pair<Integer, Integer>, ChessStyle> gameGridValues = new HashMap<>();
        GridCache curGridCache;
    }

    enum ChessStyle {
        EMPTY(0),
        BLACK_CIRCLE(1),
        WHITE_CIRCLE(2),
        RED_CIRCLE(3),
        BLUE_CIRCLE(4),
        YELLOW_CIRCLE(5),
        GREEN_CIRCLE(6),
        GREY_CIRCLE(7),
        YIN_YANG(8),
        WALL(9);

        public final int value;

        ChessStyle(int value) {
            this.value = value;
        }

        public static ChessStyle from(int value) {
            for (ChessStyle style : ChessStyle.values()) {
                if (style.value == value) {
                    return style;
                }
            }
            return EMPTY;
        }

        public void addTo(List<Sign> signList, final int[][] grids, int row, int col) {
            switch (this) {
                case EMPTY:
                case BLACK_CIRCLE:
                case WHITE_CIRCLE:
                case RED_CIRCLE:
                case BLUE_CIRCLE:
                case YELLOW_CIRCLE:
                case GREEN_CIRCLE:
                case GREY_CIRCLE:
                case YIN_YANG:
                case WALL:
                    grids[row][col] = this.value;
                    break;
            }
        }
    }
}
